﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Security.Cryptography;
using System.Threading;
using System.IO;
using Win7_Taskbar_Progressbar;

namespace WinPackMan_UpdateFinder
{
    
    public partial class Form1 : Form
    {
        Win7TaskbarProgressbar win7bar = new Win7TaskbarProgressbar();

        public Dictionary<int, data> AppData = new Dictionary<int, data>();
        public Dictionary<int, int> IndexToID = new Dictionary<int, int>();
        public Dictionary<int, int> IndexToIndex = new Dictionary<int, int>();

        LogData log = new LogData();

        Int32 LastLog = 0;
        int maxthreads = Environment.ProcessorCount*3;

        const string dataurl = "http://winpackman.mtsgrit.nl/testdata.php";

        public Form1()
        {
            InitializeComponent();
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            System.Diagnostics.Stopwatch speedtest = new System.Diagnostics.Stopwatch();
            speedtest.Start();
            int usethreads = maxthreads;
            if (AppData.Count < maxthreads) usethreads = AppData.Count;
            foreach (data item in AppData.Values)
            {
                item.NewVersion = 0;
            }
            int count = 0;
            worker[] workers = new worker[usethreads];
            Thread[] threads = new Thread[usethreads];

            for (int i = 0; i < usethreads; i++)
            {
                workers[i] = new worker(AppData[0], i, log);
                threads[i] = new Thread(workers[i].DoWork);
                log.add("Make thread " + (i+1));
            }

            while (count < AppData.Count)
            {
                for (int i = 0; i < usethreads; i++)
                {
                    if (threads[i].ThreadState != ThreadState.Running)
                    {
                        if (count == AppData.Count) break;
                        workers[i] = new worker(AppData[count], i, log);
                        threads[i] = new Thread(workers[i].DoWork);
                        log.add("Give thread " + (i + 1) + ": " + AppData[count].name, AppData[count].AppID);
                        threads[i].Start();
                        count++;
                        backgroundWorker1.ReportProgress(count / AppData.Count * 100); 
                    }
                }
                Thread.Sleep(5);
            }
            for (int i = 0; i < usethreads; i++)
            {
                if (threads[i].ThreadState == ThreadState.Running)
                {
                    Thread.Sleep(10);
                    i--;
                }
                backgroundWorker1.ReportProgress(count / AppData.Count * 100);
            }
            speedtest.Stop();
            log.add("All task done in " + speedtest.ElapsedMilliseconds + "ms");
            backgroundWorker1.ReportProgress(count / AppData.Count * 100);
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            bgwupdate();
            toolStrip1.Enabled = true;
            win7bar.SetBar(Win7TaskbarProgressbar.ProgressState.NoProgress);
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            bgwupdate();
        }
        void bgwupdate()
        {
            int count = 0;
            for (int i = 0; i < AppData.Count; i++)
            {
                if (AppData[i].NewVersion != 0)
                {
                    count++;
                    if (listView1.Items[i].BackColor == SystemColors.Window)
                    {
                        if (AppData[i].NewVersion == 1) listView1.Items[i].BackColor = Color.Red;
                        else listView1.Items[i].BackColor = Color.LightGreen;
                    }
                }

            }
            toolStripProgressBar1.Value = count;
            win7bar.SetBar(count, AppData.Count);
            if (LastLog < log.data.Count)
            {
                for (int i = LastLog; i < log.data.Count; i++)
                {
                    addlogview(i);
                    LastLog++;
                }
            }
        }

        void addlogview(int id)
        {
            String time = "";
            if (log.data[id].time.Millisecond < 100) time += "0";
            if (log.data[id].time.Millisecond < 10) time += "0";
            time += log.data[id].time.Millisecond;
            ListViewItem lvi = new ListViewItem(log.data[id].time.ToLongTimeString() + ":" + time);
            lvi.SubItems.Add((log.data[id].thread + 1).ToString());
            lvi.SubItems.Add(log.data[id].Description);
            listView2.Items.Add(lvi);
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            win7bar.ContainerControl = this;
            win7bar.SetBar(Win7TaskbarProgressbar.ProgressState.NoProgress);
            update();
            loadlistview();
            log.add("Default max threads: " + maxthreads);
            bgwupdate();
        }

        private void update()
        {
            WebClient client = new WebClient();
            String[] raw = client.DownloadString(dataurl).Split("\n".ToArray());
            AppData.Clear();
            IndexToID.Clear();
            foreach (String app in raw)
            {
                if (app != "")
                {
                    String[] rawdata = app.Split(";".ToCharArray());
                    data newitem = new data();
                    newitem.AppID = Int32.Parse(rawdata[0]);
                    newitem.name = rawdata[1];
                    newitem.URL = rawdata[2];
                    newitem.ReadF = Int32.Parse(rawdata[3]);
                    newitem.ReadT = Int32.Parse(rawdata[4]);
                    newitem.SHA1 = rawdata[5];
                    newitem.Find = rawdata[6];
                    IndexToID.Add(AppData.Count, newitem.AppID);
                    AppData.Add(AppData.Count, newitem);
                }
            }
            log.add("Update list");
            bgwupdate();
            toolStripProgressBar1.Maximum = AppData.Count;
        }

        private void loadlistview()
        {
            listView1.Items.Clear();
            foreach (data app in AppData.Values)
            {
                ListViewItem lvi = new ListViewItem(app.name);
                lvi.SubItems.Add(app.URL);
                lvi.SubItems.Add(app.ReadF + "-" + app.ReadT);
                lvi.SubItems.Add(app.SHA1);
                lvi.SubItems.Add(app.Find);
                listView1.Items.Add(lvi);
            }
        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            win7bar.SetBar(Win7TaskbarProgressbar.ProgressState.Normal);
            foreach (ListViewItem item in listView1.Items)
                item.BackColor = SystemColors.Window;
            toolStrip1.Enabled = false;
            backgroundWorker1.RunWorkerAsync();
        }

        private void listView2_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (listView2.SelectedItems.Count > 0)
            {
                int item = listView2.SelectedItems[0].Index;
                if (IndexToIndex.Count > 0) item = IndexToIndex[item];
                if (log.data[item].Copydata != "")
                {
                    Clipboard.SetText(log.data[item].Copydata);
                    MessageBox.Show("'" + log.data[item].Copydata + "' to clipboard");
                }
            }
        }

        private void toolStripButton2_Click(object sender, EventArgs e)
        {
            update();
            loadlistview();
        }

        private void toolStripButton3_Click(object sender, EventArgs e)
        {
            log.data.Clear();
            listView2.Items.Clear();
            LastLog = 0;
        }

        private void listView1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (listView1.SelectedItems.Count > 0)
            {
                IndexToIndex.Clear();
                listView2.Items.Clear();
                int id = IndexToID[listView1.SelectedItems[0].Index];
                int count = 0;
                for (int i = 0; i < log.data.Count; i++)
                {
                    if (log.data[i].AppID == id)
                    {
                        addlogview(i);
                        IndexToIndex.Add(count, i);
                        count++;
                    }
                }
            }
        }

        private void toolStripButton4_Click(object sender, EventArgs e)
        {
            IndexToIndex.Clear();
            listView2.Items.Clear();
            for (int i = 0; i < log.data.Count; i++)
                addlogview(i);
        }

        private void configToolStripMenuItem_DropDownOpening(object sender, EventArgs e)
        {
            configToolStripMenuItem.DropDownItems.Clear();
            ToolStripMenuItem item = new ToolStripMenuItem("Max threads");
            for (int i = 1; i < 6; i++)
            {
                int count = Environment.ProcessorCount * i;
                ToolStripMenuItem subitem = new ToolStripMenuItem(count.ToString());
                subitem.Tag = count.ToString();
                subitem.Click +=new EventHandler(SetMaxThreads);
                item.DropDownItems.Add(subitem);
            }
            item.DropDownItems.Add(new ToolStripSeparator());
            for (int i = 0; i < 8; i++)
            {
                double count = System.Math.Pow(2, i);
                ToolStripMenuItem subitem = new ToolStripMenuItem(count.ToString());
                subitem.Tag = count.ToString();
                subitem.Click += new EventHandler(SetMaxThreads);
                item.DropDownItems.Add(subitem);
            }
            configToolStripMenuItem.DropDownItems.Add(item);
            
        }
        private void SetMaxThreads(object sender, EventArgs e)
        {
            ToolStripMenuItem item = (ToolStripMenuItem)sender;
            maxthreads = Int32.Parse(item.Tag.ToString());
            log.add("Set maxthread to " + maxthreads);
            bgwupdate();
        }

        private void openEditWebsiteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (listView1.SelectedItems.Count > 0)
                System.Diagnostics.Process.Start("http://winpackman.mtsgrit.nl/edit.php?ID=" + IndexToID[listView1.SelectedItems[0].Index]);
        }

        private void testThisItemToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (listView1.SelectedItems.Count > 0){
                log.add("Start single test", AppData[listView1.SelectedItems[0].Index].AppID);
                worker work = new worker(AppData[listView1.SelectedItems[0].Index], -1, log);
                work.DoWork();
                bgwupdate();
                listView1.SelectedItems[0].Selected = false;
            }
        }
    }

    public class data
    {
        public Int32 AppID = 0;
        public String name = "";
        public String URL = "";
        public Int32 ReadF = 0;
        public Int32 ReadT = 0;
        public String SHA1 = "";
        public String Find = "";
        public Int16 NewVersion = 0;
    }

    public class LogData
    {
        public Dictionary<int, logclass> data = new Dictionary<int, logclass>();

        public void add(String Description)
        {
            add(-1, Description, "",0);
        }

        public void add(String Description, Int32 ID)
        {
            add(-1, Description, "", ID);
        }

        public void add(Int32 thread, String Description, Int32 ID)
        {
            add(thread, Description, "", ID);
        }

        public void add(Int32 thread, String Description, String Copy, Int32 ID)
        {
            logclass logitem = new logclass();
            logitem.Description = Description;
            logitem.thread = thread;
            logitem.Copydata = Copy;
            logitem.AppID = ID;
            data.Add(data.Count, logitem);
        }

        public class logclass
        {
            public DateTime time = DateTime.Now;
            public Int32 thread = 0;
            public String Description = "";
            public String Copydata = "";
            public Int32 AppID = 0;
        }
    }


    public class worker
    {
        private data item;
        private int Thread;
        private LogData log;

        public worker(data dowork, int ThreadID, LogData logdata)
        {
            item = dowork;
            Thread = ThreadID;
            log = logdata;
        }

        public void DoWork()
        {
            System.Diagnostics.Stopwatch speedtest = new System.Diagnostics.Stopwatch();
            speedtest.Start();
            try
            {
                Boolean newversionA = true;
                Boolean newversionB = true;
                WebClient client = new WebClient();
                client.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)");
                String[] downloaddata = client.DownloadString(item.URL).Split("\n".ToArray());
                log.add(Thread, "Done downloading website", item.AppID);
                if (item.SHA1 != "")
                {
                    string text = "";
                    for (int i = item.ReadF; i < item.ReadT; i++)
                        text += downloaddata[i];
                    string SHA1 = CalculateSHA1(text, Encoding.Default);
                    if (SHA1 == item.SHA1) newversionA = false;
                    else log.add(Thread, "SHA1 different, new: " + SHA1, SHA1, item.AppID);
                }
                else newversionA = false;
                if (item.Find != "")
                {
                    foreach (String line in downloaddata)
                        if (line.Contains(item.Find)) newversionB = false;
                    if (newversionB)
                        log.add(Thread, "String not find in website", item.AppID);
                }
                else newversionB = false;
                speedtest.Stop();
                if (newversionA || newversionB)
                {
                    item.NewVersion = 1;
                    log.add(Thread, "Done in " + speedtest.ElapsedMilliseconds + "ms, possible new version available", item.AppID);
                }
                else
                {
                    item.NewVersion = 2;
                    log.add(Thread, "Done in " + speedtest.ElapsedMilliseconds + "ms, no new version", item.AppID);
                }
            }
            catch (Exception e)
            {
                item.NewVersion = 1;
                log.add(Thread, "Error: " + e.ToString(), item.AppID);
            }
        }


        private static string CalculateSHA1(String text, Encoding enc)
        {
            byte[] buffer = enc.GetBytes(text);
            SHA1CryptoServiceProvider cryptoTransformSHA1 =
            new SHA1CryptoServiceProvider();
            string hash = BitConverter.ToString(
                cryptoTransformSHA1.ComputeHash(buffer)).Replace("-", "");

            return hash;
        }
    }
}
